/*
 Basic ESP8266 MQTT example
 This sketch demonstrates the capabilities of the pubsub library in combination
 with the ESP8266 board/library.
 It connects to an MQTT server then:
  - publishes "hello world" to the topic "outTopic" every two seconds
  - subscribes to the topic "inTopic", printing out any messages
    it receives. NB - it assumes the received payloads are strings not binary
  - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
    else switch it off
 It will reconnect to the server if the connection is lost using a blocking
 reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
 achieve the same result without blocking the main loop.
 To install the ESP8266 board, (using Arduino 1.6.4+):
  - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
       http://arduino.esp8266.com/stable/package_esp8266com_index.json
  - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
  - Select your ESP8266 in "Tools -> Board"
*/

#define WIFISSID "dlink"
#define PASSWORD "dlink201"
#define CLIENTID "test"
#define MQTTSERVER "119.91.90.201"
#define WIFI
#define BUFFER_SIZE 801

#define D0 9
#define D2 10
#define D3 11
#define D8 12

#ifdef WIFI
#include <WiFi.h>
#include <WiFiUdp.h>
#else
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#endif

#include <PubSubClient.h>
#include <IRsend.h>
#include <EEPROM.h>

#define MAGICTWORD "IOTCONFIGV2"
struct TConfig{
  char magictword[16];
  char wifissid[16];
  char password[16];
  char clientid[16];
  char ip[16];
  char dns[16];
  char gateway[16];
  char mqtt_server[16];
  char enabled[4];
  char mac[6];
};

#define CONFIG_LENGTH (sizeof(TConfig))
union 
{
  TConfig data;
  byte b[CONFIG_LENGTH];
} config;

byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 177);
IPAddress dns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
char wifissid[16]=WIFISSID;
char password[16]=PASSWORD;
char clientid[16]=CLIENTID;
char mqtt_server[16]=MQTTSERVER;

char wolcmd[32];
char blinkcmd[32];
char ircmd[4][32];
byte IREnabled[4] = {1, 1, 0, 0};

IRsend irsend1(4);
IRsend irsend2(D8);
IRsend irsend3(D0);
IRsend irsend4(D3);//这个端口需要接5k电阻，否则会导致芯片不能正常启动。
IRsend *pirsend[4];

// Update these with values suitable for your network.

#ifdef WIFI
WiFiClient espClient;
WiFiUDP Udp;
PubSubClient client(espClient);
#else
EthernetClient ethclient;
PubSubClient client(ethclient);
EthernetUDP Udp;
#endif

unsigned int mqtt_port = 1883;

//unsigned long lastMsg = 0;
//#define MSG_BUFFER_SIZE  (50)
//char msg[MSG_BUFFER_SIZE];
//int value = 0;
#ifdef WIFI
void network_setup() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(wifissid);

  WiFi.mode(WIFI_STA);
  //WiFi.config(mac, ip, dns, gateway);
  WiFi.begin(wifissid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}
#else
void network_setup() {
  Ethernet.init(5);

  while (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // Check for Ethernet hardware present
    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
      Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
      while (true) {
        delay(1); // do nothing, no point running without Ethernet hardware
      }
    }
    if (Ethernet.linkStatus() == LinkOFF) {
      Serial.println("Ethernet cable is not connected.");
    }
    // try to congifure using IP address instead of DHCP:
    //Ethernet.begin(mac, ip, dns, gateway);
//    Serial.print("IP:");
//    Serial.println(ip);
//    Serial.print("DNS:");
//    Serial.println(dns);
//    Serial.print("Gateway:");
//    Serial.println(gateway);
//  } else {
//    Serial.print("  DHCP assigned IP ");
//    Serial.println(Ethernet.localIP());
    Serial.print(".");
    delay(500);
  }    
  Serial.print("DHCP assigned IP ");
  Serial.println(Ethernet.localIP());
}
#endif


void IRcontrol(IRsend *pirsend, uint16_t rawData[], int dataLength)
{
  
  digitalWrite(LED_BUILTIN, LOW);
  for (int i = 0; i < dataLength; i++) {
    Serial.print(rawData[i]);
    Serial.print(',');
    if (i%8 == 7) Serial.println();
  }
  Serial.println();
  pirsend->sendRaw(rawData, dataLength, 38);
  delay(200);
  pirsend->sendRaw(rawData, dataLength, 38);
  digitalWrite(LED_BUILTIN, LOW);

}

//唤醒主机
void wakeupPC(byte destMac[])
{
    byte all[102];
    int i,c1,j=0;

    IPAddress remoteIp(255,255,255,255);
    
  digitalWrite(LED_BUILTIN, LOW);
  Serial.println("Sending WOL Packet...");
  for(i=0; i<6; i++)
    Serial.print(destMac[i], HEX);
  Serial.println();

  for(i = 0; i < 6; i++,j++){
    all[j] = 0xFF;
  }
  for(i = 0; i < 16; i++){
    for( c1 = 0; c1 < 6; c1++,j++)
      all[j] = destMac[c1];
  }

  Udp.begin(8888);
  Udp.beginPacket(remoteIp, 9);
  Udp.write(all, 102);
  Udp.endPacket();

  digitalWrite(LED_BUILTIN, HIGH);
}


void led_blink()
{
  for(int i=0; i<3; i++)
  {
     digitalWrite(LED_BUILTIN, LOW);
     delay(500);
     digitalWrite(LED_BUILTIN, HIGH);
     delay(500);
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] length: ");
  Serial.println(length);

  if (strcmp(topic, blinkcmd) == 0)
  {
    led_blink();
  }
  else if (strcmp(topic, wolcmd) == 0)
  {
    wakeupPC(payload);
  }
  else
  {
      for(int i=0; i<4; i++)
      {
        if (strcmp(topic, ircmd[i])==0)
          IRcontrol(pirsend[i], (uint16_t*)payload, length/sizeof(uint16_t));
      }


  }

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID

    // Attempt to connect
    if (client.connect(clientid)) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      //client.publish("outTopic", "hello world");
      // ... and resubscribe
      
      for(int i=0; i<4; i++)
      {
        if (IREnabled[i])
        {
          client.subscribe(ircmd[i]);
        }
      }

      client.subscribe(wolcmd);
      client.subscribe(blinkcmd);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void load_eeprom() {
  EEPROM.begin(4095); //申请操作到地址4095（比如你只需要读写地址为100上的一个字节，该处也需输入参数101）
  for (int addr = 0; addr < CONFIG_LENGTH; addr++)
  {
    config.b[addr] = EEPROM.read(addr); //读数据
  }

  if (strcmp(config.data.magictword, MAGICTWORD)==0)
  {
    Serial.println("EEPROM Loaded.");
    strcpy(wifissid, config.data.wifissid);
    strcpy(password, config.data.password);
    strcpy(clientid, config.data.clientid);
    strcpy(mqtt_server, config.data.mqtt_server);
    memcpy(IREnabled, config.data.enabled, 4);
    memcpy(mac, config.data.mac, 6);
    ip.fromString(config.data.ip);
    dns.fromString(config.data.dns);
    gateway.fromString(config.data.gateway);
  }
}

void show_config()
{
  Serial.print("wifissid:");
  Serial.println(wifissid);

  Serial.print("password:");
  Serial.println(password);

  Serial.print("client id:");
  Serial.println(clientid);

  Serial.print("mqtt server:");
  Serial.println(mqtt_server);

  Serial.print("mac:");
  for(int i=0; i<6; i++) Serial.print(mac[i], HEX);
  Serial.println();
  
  Serial.print("IR Enabled:");
  for(int i=0; i<4; i++) Serial.print(IREnabled[i], HEX);
  Serial.println();
}

void setup() {
  Serial.begin(115200);

  load_eeprom();
  show_config();
  
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  network_setup();
  client.setBufferSize(BUFFER_SIZE);
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);

  pirsend[0] = &irsend1;
  pirsend[1] = &irsend2;
  pirsend[2] = &irsend3;
  pirsend[3] = &irsend4;

  sprintf(wolcmd, "%s/wol", clientid);
  sprintf(blinkcmd, "%s/blink", clientid);
  for(int i=0; i<4; i++)
  {
    if (IREnabled[i])
    {
      pirsend[i]->begin();
      sprintf(ircmd[i], "%s/ir/%d", clientid, i+1);
      Serial.println(ircmd[i]);
    }
  }

}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}
